package com.fourhats.services.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.dozer.Mapper;
import org.springframework.beans.factory.annotation.Autowired;

import com.fourhats.dao.CategoryDAO;
import com.fourhats.dao.MarketDAO;
import com.fourhats.dao.ProductBuyedDAO;
import com.fourhats.dao.ProductDAO;
import com.fourhats.dao.UserDAO;
import com.fourhats.dao.VisitEntityDAO;
import com.fourhats.model.Category;
import com.fourhats.model.Image;
import com.fourhats.model.Market;
import com.fourhats.model.ObjectTypes;
import com.fourhats.model.Product;
import com.fourhats.model.ProductBuyed;
import com.fourhats.model.User;
import com.fourhats.model.VisitEntity;
import com.fourhats.services.ProductService;
import com.fourhats.services.dto.ImageDto;
import com.fourhats.services.dto.ProductBuyedDto;
import com.fourhats.services.dto.ProductDto;
import com.fourhats.services.util.Page;

public class ProductServiceImpl implements ProductService {

	@Autowired
	private Mapper mapper;
	
	@Autowired
	private ProductDAO productDAO;
	
	@Autowired
	private UserDAO userDAO;
	
	@Autowired
	private ProductBuyedDAO productBuyedDAO;
	
	@Autowired
	private CategoryDAO categoryDAO;
	
	@Autowired
	private MarketDAO marketDAO;
	
	@Autowired
	private VisitEntityDAO visitEntityDAO;
	
	public void addProduct(ProductDto product) {
		Category category = categoryDAO.getById(product.getCategoryId());
		Market market = marketDAO.getById(product.getMarketId());
		Product newProduct = new Product(product.getName(), product.getSubtitle(), product.getDescription(), product.getPrice(), product.isNegotiablePrice(), product.getQuantity(), category);
		
		for(ImageDto img : product.getImages()){
			Image image = new Image();
			image.setPath(img.getPath());
			image.setIsPrincipal(img.getIsPrincipal());
			newProduct.getImages().add(image);
		}
		
		newProduct.setMarket(market);
		productDAO.add(newProduct);
	}
	
	public void editProduct(ProductDto product) {
		Category category = categoryDAO.getById(product.getCategoryId());
		
		Product productToEdit = productDAO.getById(product.getId());
		productToEdit.setInformation(product.getName(), product.getSubtitle(), product.getDescription(), product.getPrice(), product.isNegotiablePrice(), product.getQuantity(), category);
		
		Set<Image> images = new HashSet<Image>();
		for(ImageDto i : product.getImages()) {
			images.add(mapper.map(i, Image.class));
		}
		
		productToEdit.updateImages(images);
		
		productDAO.add(productToEdit);
	}
	
	public List<ProductDto> getProductsByKeyword(String keyWord) {
		List<Product> products = productDAO.getProductsByKeyword(keyWord);
		return this.getDtos(products);
	}
	
	public Page<ProductDto> getProductsPageByKeyword(String keyword, int pageNumber, int pageSize) {
		List<Product> products = productDAO.getProductsByKeyword(keyword);
		return this.getPage(products, pageNumber, pageSize);
	}

	public ProductDto getProductById(long productId) {
		return this.getDto(productDAO.getById(productId));
	}

	public List<ProductDto> getRandomlyHighlightProducts(int maxResult) {
		List<Product> products = productDAO.getRandomlyHighlightProducts(maxResult);
		return this.getDtos(products);
	}
	
	@SuppressWarnings("unchecked")
	public Page<ProductDto> getProductsByCategory(long categoryId, int pageNumber, int pageSize, String orderBy) {
		if(orderBy != null && orderBy.equals("MostView")) {
			List<ProductDto> products = this.getDtos(productDAO.getProductsByCategory(categoryId));
			Collections.sort(products, new Comparator<ProductDto>() {
				public int compare(ProductDto p1, ProductDto p2) {
					return p2.getQtyVisits().compareTo(p1.getQtyVisits());
				}
			});
			
			return (Page<ProductDto>) Page.create(products, pageNumber, pageSize);
		} else {
			return this.getPage(productDAO.getProductsByCategory(categoryId), pageNumber, pageSize);
		}
	}
	
	public Page<ProductDto> getProductsByMarketOrdered(long marketId, int pageNumber, int pageSize) {
		List<Product> products = productDAO.getProductsByEnabledMarket(marketId);
		return this.getPage(products, pageNumber, pageSize);
	}
	
	public Page<ProductDto> getProductsByMarket(long marketId, int pageNumber, int pageSize) {
		List<Product> products = productDAO.getProductsByMarket(marketId);
		return this.getPage(products, pageNumber, pageSize);
	}
	
	@SuppressWarnings("unchecked")
	public Page<ProductDto> getAllProducts(int pageNumber, int pageSize, String orderBy) {
		if(orderBy != null && orderBy.equals("MostView")) {
			List<ProductDto> products = this.getDtos(productDAO.getAllProducts());
			Collections.sort(products, new Comparator<ProductDto>() {
				public int compare(ProductDto p1, ProductDto p2) {
					return p2.getQtyVisits().compareTo(p1.getQtyVisits());
				}
			});
			
			return (Page<ProductDto>) Page.create(products, pageNumber, pageSize);
		} else {
			return this.getPage(productDAO.getAllProducts(), pageNumber, pageSize);
		}
	}
	
	public boolean purchaseProduct(long productId, long userId) {
		Product product = this.productDAO.getById(productId);
		User user = this.userDAO.getById(userId);
		if(!product.isEnable() || product.getQuantity() < 1 || !user.isEnable() 
				|| !product.getMarket().isEnable() || product.getMarket().getUser().getId().equals(userId)) {
			return false;
		}
		
		ProductBuyed pb = new ProductBuyed(product, user);
		this.productBuyedDAO.add(pb);
		product.buy();
		this.productDAO.add(product);
		
		return true;
	}
	
	public void deleteProduct(long productId) {
		Product product = this.productDAO.getById(productId);
		product.setEnable(false);
		
		this.productDAO.add(product);
	}
	
	public Page<ProductBuyedDto> getPurchasedProducts(long userId, int pageNumber, int pageSize) {
		List<ProductBuyed> purchasedProducts = productBuyedDAO.getPurchasedProducts(userId);		
//		List<Product> products = new ArrayList<Product>();
//		
//		for(ProductBuyed purchased : purchasedProducts){
//			products.add(purchased.getProduct());
//		}
//		
		return getProductBuyedPage(purchasedProducts, pageNumber, pageSize);
	}
	
	public Page<ProductBuyedDto> getSoldProducts(long userId, int pageNumber, int pageSize) {
		List<ProductBuyed> soldProducts = productBuyedDAO.getSoldProducts(userId);		
//		List<Product> products = new ArrayList<Product>();
//		
//		for(ProductBuyed sold : soldProducts){
//			products.add(sold.getProduct());
//		}
		
		return getProductBuyedPage(soldProducts, pageNumber, pageSize);
	}
	
	public boolean hasUnvisitedSoldProducts(long userId) {
		VisitEntity visitEntity = this.visitEntityDAO.getLastByUserAndType(userId, ObjectTypes.QUESTION_PROFILE.getValue());
		if(visitEntity == null) {
			return this.productBuyedDAO.getSoldProducts(userId).size() > 0;
		}
		
		return this.productBuyedDAO.getSoldProductsSince(userId, visitEntity.getDate()).size() > 0;
	}
	
	private List<ProductDto> getDtos(List<Product> products) {
		List<ProductDto> dtos = new ArrayList<ProductDto>();
		for(Product p : products) {
			dtos.add(this.getDto(p));
		}
		
		return dtos;
	}
	
	private List<ProductBuyedDto> getProductBuyedDtos(List<ProductBuyed> products) {
		List<ProductBuyedDto> dtos = new ArrayList<ProductBuyedDto>();
		for(ProductBuyed p : products) {
			dtos.add(this.getProductBuyedDto(p));
		}
		
		return dtos;
	}
	
	private ProductDto getDto(Product product) {
		ProductDto productDto = mapper.map(product, ProductDto.class);
		return this.populateProduct(productDto);
	}
	
	private ProductBuyedDto getProductBuyedDto(ProductBuyed product) {
		return mapper.map(product, ProductBuyedDto.class);
	}
	
	@SuppressWarnings("unchecked")
	private Page<ProductDto> getPage(List<Product> products, int pageNumber, int pageSize) {
		int first = (pageNumber - 1) * pageSize;
		int last = pageNumber * pageSize > products.size() ? products.size() : pageNumber * pageSize;
		return (Page<ProductDto>) Page.create(this.getDtos(products.subList(first, last)), pageNumber, pageSize, products.size());
	}
	
	@SuppressWarnings("unchecked")
	private Page<ProductBuyedDto> getProductBuyedPage(List<ProductBuyed> products, int pageNumber, int pageSize) {
		int first = (pageNumber - 1) * pageSize;
		int last = pageNumber * pageSize > products.size() ? products.size() : pageNumber * pageSize;
		return (Page<ProductBuyedDto>) Page.create(this.getProductBuyedDtos(products.subList(first, last)), pageNumber, pageSize, products.size());
	}
	
	private ProductDto populateProduct(ProductDto product) {
		product.setQtyVisits(this.visitEntityDAO.getQtyVisits(product.getId(), ObjectTypes.PRODUCT.getValue()));
		return product;
	}
}